home *** CD-ROM | disk | FTP | other *** search
- /*
-
- Compiled and tested with Borland C++ 3.0, 3.1
- Uses INLINE assembly (ick)!
-
- Support com1 thru 4 at standard addresses and IRQ's,
- 300 ->115200 baud
-
- This file contains time tested serial port I/O
- stuff. Most of this came from "Al Williams: DOS 5 for Developers" book
- which is excelent! The serial code in the book had one minor bug
- which caused chars to be dropped rarely, but that is fixed.
-
- To use with a modem you need a few simple routines to dial etc...
- but this just involes sending the correct strings to the modem and listening
- for the response.
-
- .e.g. send ATDT555-555 to the modem, then wait or a respones
- which will be something like CONNECT, BUSY, NO DIALTONE, etc...
- don't wait forever :) for a response!
-
- Feel free to use this in any project, commercial or otherwise.
-
- Alec Russell.
-
- */
-
- /* start of serio.h -------------------------------------------------- */
-
- /****************************************************************
- * *
- * File: serio.h *
- * *
- * Description: *
- * Header file for programs that use SERIO.C *
- * *
- ****************************************************************/
-
- #ifndef SERIOHEADER
- #define SERIOHEADER
-
- /* define for sio_error */
- enum SIO_ERROR_CODES
- {
- SIO_IS_OK, /* 0 == NO ERROR */
- SIO_OVERFLOW,
- SIO_NO_COMM,
- SIO_BAD_BAUD,
- SIO_NO_IRQ,
- SIO_BAD_BITS,
- SIO_BAD_PARITY
- };
-
- typedef struct
- {
- int comport; /* comm 1,2,3,4 */
- int irq; /* irq, set to 0 for default */
- int baud; /* see below */
- int bits; /* data bits */
- int parity; /* see below */
- int stops; /* stop bits */
- }
- serial_232_t;
-
- /*
- baud:
-
- 0 - 110
- 1 - 150
- 2 - 300
- 3 - 1200
- 4 - 2400
- 5 - 4800
- 6 - 9600
- 7 - 19200
-
- parity:
- 0 - none
- 1 - odd
- 2 - even
- 3 - mark
- 4 - space
-
- */
-
- enum
- {
- BAUD_110,
- BAUD_150,
- BAUD_300,
- BAUD_1200,
- BAUD_2400,
- BAUD_4800,
- BAUD_9600,
- BAUD_19200,
- BAUD_38400,
- BAUD_57600,
- BAUD_115200
- };
-
- enum
- {
- PARITY_NONE,
- PARITY_ODD,
- PARITY_EVEN,
- PARITY_MARK,
- PARITY_SPACE
- };
-
- /*
-
- ansi_str[]
-
- 0 Up
- 1 Down
- 2 Right
- 3 Left
- 4 home
- 5 end
- 6 pg up
- 7 pg dn
- 8 ... undefined
-
- */
-
- /* ------------------ misc functions ------------------ */
-
- /* Send a break */
- void sio_sendbreak(void);
-
-
- /* Check for character available (non-zero means char ready) */
- int char_ready_232(void);
-
-
- /* ------------- main functions ------------------------ */
-
- /* wait until char is ready to be read */
- int get_232_wait(void);
-
- /* returns -2 for break, -1 if no char ready */
- int get_232(void);
-
- int peek_232x(void);
- int peek_232(void);
-
-
- /* write a character. returns 0 if successful, -1 if error */
- int send_232(unsigned int c);
-
- /* startup comport, install intrpt handler */
- int init_232(serial_232_t *sr);
-
- /* CALL THIS FUNCTION BEFORE PROGRAM EXIT! */
- void deinit_232(void);
-
- /* clear transmit, and recieve ques */
- void flush_232_xmit(void);
- void flush_232_rcv(void);
-
-
- /* End of header */
- #endif
-
- /* ---------------- end of serio.h ------------------------------------ */
-
- /* --------------------- start of serial.c ---------------------------- */
- /*
-
- Alec Russell
-
- low level serial stuff
-
- Nov 30, 1992 - AR fixed a bug causing chars to be missed when transmiting
- in send_232()
-
- */
-
- #pragma inline
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <time.h>
- #include <ctype.h>
- #include <dos.h>
- #include <bios.h>
-
- #include "serio.h"
-
- #define TICKS (*(volatile unsigned long far *)(0x0040006CL))
-
- static void xmit(void);
- static void rcv(void);
- void msr(void);
- static void linestat(void);
-
- static unsigned char force_transmit_ready; /* flag to indicate tranmitter needs service */
-
-
- #define BUFFERLEN 2048
-
- /* ticks to hold a break */
- #define BRKTIME 5
-
-
- /* base address of UART I/O */
- static int uart_adrs;
-
-
- /* IRQ & mask bit for PIC */
- static int irqmask, irqnum;
-
-
- #define XOFF 19
- #define XON 17
-
-
- /* global variables of interest to user's program */
- int sio_error=0; /* see serio.h sio_error_codes */
- int sio_errct=0; /* overflow count */
-
-
- /* set to 1 when break occurs */
- int sio_break=0;
-
-
- unsigned char sio_linestat; /* line status */
-
-
- unsigned char sio_modemstat; /* modem status */
-
-
- /* --- turn xon/xoff on off with this */
- // int sio_doxoff=0; /* set xon/xoff mode on */
-
-
- /* if 1, get_232() returns -2 if break occured */
- int sio_brkmode=0;
-
-
- #define DATA_REG 0
- /* interrupt enable register */
- #define IER 1
- #define IIR 2 /* interrupt id register */
- #define LCR 3 /* line control register */
- #define MCR 4 /* modem control register */
- #define LSR 5 /* line status register */
- #define MSR 6 /* modem status register */
-
- #define MCR_RTS 0x02 /* MCR register RTS bit */
-
- /* Define two circular buffers */
- static unsigned char xmitbuf[BUFFERLEN+1];
- static unsigned char rcvbuf[BUFFERLEN+1];
-
-
- static unsigned int xmithead=0,xmittail=0;
- static unsigned int xmitlen=0;
-
-
-
-
- static unsigned int rcvhead=0,rcvtail=0;
- static unsigned int rcvlen=0;
-
-
- // static int sentxoff=0,sendxoff=0,sendxon=0;
- // static int gotxoff=0;
-
- static void (interrupt far *oldirq)(void);
-
- /* baud rate table */
- static unsigned int baud_table[]=
- {
- 0x417, /* 110 baud */
- 0x300, /* 150 baud */
- 0x180, /* 300 baud */
- 0x60, /* 1200 baud */
- 0x30, /* 2400 baud */
- 0x18, /* 4800 baud */
- 0xc, /* 9600 baud */
- 0x6, /* 19200 baud hmm... is the word "baud" redundant? */
- 0x3, /* 38400 */
- 0x2, /* 57600 */
- 0x1 /* 115200 */
- };
-
- // NOTE!!!!!!!!!
- // there is a way to look these up from the BIOS, but it isn't very reliable
- // so I hard coded these addresses!
-
- static unsigned int irq_adr_table[]=
- {
- 0x3f8,
- 0x2f8,
- 0x3e8,
- 0x2e8
- };
-
- /* ===================================================================== */
-
- /* ------------------ start basic serial of functions ------------------ */
-
-
- /*
- returns:
- 0 if ok
- 1 if less than FILL space bytes left in buffer
- 2 if full
-
- */
- /* ---------------------- put_rcv_que() -------------------- May 29,1992 */
- int put_rcv_que(unsigned char c)
- {
- int ret_code;
-
- if ( rcvlen == BUFFERLEN )
- {
- /* throw away oldest data, put in new data */
- ret_code=2;
-
- rcvbuf[rcvtail]=c;
-
- ++rcvhead;
- if ( rcvhead == BUFFERLEN )
- rcvhead=0;
- ++rcvtail;
- if ( rcvtail == BUFFERLEN )
- rcvtail=0;
- }
- else
- {
- /* put new data in que */
- /*
- if ( rcvlen > (BUFFERLEN - FILL) )
- ret_code=1;
- else
- */
-
- ret_code=0;
-
- rcvbuf[rcvtail]=c;
- ++rcvtail;
- if ( rcvtail == BUFFERLEN )
- rcvtail=0;
- ++rcvlen;
- }
-
- return(ret_code);
- }
-
-
- /*
- returns:
- char if ok
- -1 if que empty
- */
- /* ---------------------- get_rcv_que() -------------------- May 29,1992 */
- unsigned int get_rcv_que(void)
- {
- unsigned int c;
-
- if ( rcvlen == 0 )
- return(-1);
-
- c=rcvbuf[rcvhead];
- ++rcvhead;
- if ( rcvhead == BUFFERLEN )
- rcvhead=0;
- --rcvlen;
-
- return(c);
- }
-
-
- /*
- returns:
- 0 if ok
- 1 if less than FILL space bytes left in buffer
- 2 if full
-
- */
- /* ---------------------- put_xmit_que() -------------------- May 29,1992 */
- int put_xmit_que(unsigned char c)
- {
- int ret_code;
-
- if ( xmitlen == BUFFERLEN )
- {
- /* throw away oldest data, put in new data */
- ret_code=2;
- xmitbuf[xmittail]=c;
- ++xmithead;
- if ( xmithead == BUFFERLEN )
- xmithead=0;
- ++xmittail;
- if ( xmittail == BUFFERLEN )
- xmittail=0;
- }
- else
- {
- /* put new data in que */
- /*
- if ( xmitlen > (BUFFERLEN - FILL) )
- ret_code=1;
- else
- */
-
- ret_code=0;
-
- xmitbuf[xmittail]=c;
- ++xmittail;
- if ( xmittail == BUFFERLEN )
- xmittail=0;
- ++xmitlen;
- }
-
- return(ret_code);
- }
-
-
- /*
- returns:
- char if ok
- -1 if que empty
- */
- /* ---------------------- get_xmit_que() -------------------- May 29,1992 */
- unsigned int get_xmit_que(void)
- {
- unsigned int c;
-
- if ( xmitlen == 0 )
- return(-1);
-
- c=xmitbuf[xmithead];
- ++xmithead;
- if ( xmithead == BUFFERLEN )
- xmithead=0;
-
- --xmitlen;
-
- return(c);
- }
-
-
- /* nuke rcv buffer, and clear error flags */
- /* ---------------------- flush_232_rcv() ------------------ May 28,1992 */
- void flush_232_rcv(void)
- {
- rcvhead=rcvtail=0;
- sio_error=sio_errct=0;
- rcvlen=0;
- }
-
- /* nuke xmit buffer, attempt to send everything 1st */
- /* ---------------------- flush_232_xmit() ------------------ May 28,1992 */
- void flush_232_xmit(void)
- {
- unsigned long t1;
-
- t1=TICKS;
- t1+=200;
-
- while ( xmitlen && TICKS < t1 )
- ;
-
- sio_error=sio_errct=0;
- xmithead=xmittail=0;
- xmitlen=0;
- }
-
-
-
- /*
-
- this gets called whenever the UART generates an interupt
-
- */
- /* ISR for UART interrupt */
- void far interrupt comint(void)
- {
- int intstat;
-
- force_transmit_ready = 0;
-
- intstat=inportb(uart_adrs+IIR);
-
- while ( !(intstat & 1) )
- {
- switch ( intstat & 0x07)
- {
- case 0x02:
- xmit();
- break;
-
- case 0x04:
- rcv();
- break;
-
- case 0x06:
- linestat();
- break;
-
- case 0:
- msr();
- break;
- }
-
- intstat=inportb(uart_adrs+IIR);
- }
-
-
- if ( force_transmit_ready )
- xmit();
-
- outportb(0x20,0x20); /* reset PIC */
- }
-
-
- /* Handle Modem Status interrupts - used to detect online status */
- void msr(void)
- {
- sio_modemstat=inportb(uart_adrs+MSR);
- }
-
-
- /* Handle Line Status interrupts */
- static void linestat(void)
- {
- sio_linestat=inportb(uart_adrs+LSR);
- if ( sio_linestat & 0x10 )
- sio_break=1;
- }
-
-
-
- /* Attempt to transmit character */
- static void xmit(void)
- {
- unsigned int c;
-
- force_transmit_ready = 0;
-
- if ( xmitlen == 0 )
- return;
-
- /* UART not ready to transmit */
- if ( !(inportb(uart_adrs+LSR) & 0x20) )
- return;
-
- /* send next char in the buffer */
- c=get_xmit_que();
- outportb(uart_adrs+DATA_REG, (unsigned char)c);
- }
-
-
- /* Attempt to receive a character */
- static void rcv(void)
- {
- unsigned int c, status;
-
- /* UART not ready to rcv */
- while ( (status=inportb(uart_adrs+LSR)) & 1 )
- {
- /* Correct for possible loss of transmit interrupt from IIR register */
- if ( status & 0x20 )
- force_transmit_ready = 1;
-
- if ( rcvlen == BUFFERLEN )
- {
- /* buffer overflow */
- sio_error=SIO_OVERFLOW;
- sio_errct++;
- }
-
- c=inportb(uart_adrs+DATA_REG);
- put_rcv_que((unsigned char)c);
- }
-
- return;
- }
-
- /* delay used for sending a break */
- static void t_delay(int ticks)
- {
- clock_t tick0;
-
- tick0=clock()+ticks;
-
- while ( clock() < tick0 )
- ;
- }
-
-
- /* write a character. returns 0 if successful, -1 if error */
- int send_232(unsigned int c)
- {
-
- /* wait until some room in buffer */
- while ( xmitlen == BUFFERLEN )
- ;
-
- put_xmit_que(c);
-
- /* Call xmit in case transmitter has been idle for
- a while. If transmitter is busy, xmit won't do
- anything
- */
-
- asm cli
-
- xmit();
-
- asm sti
-
- return 0;
- }
-
-
- /* Check for character available (non-zero means char ready) */
- int char_ready_232(void)
- {
- return rcvlen;
- }
-
-
- /* returns -2 for break, -1 if no char ready */
- int get_232(void)
- {
- int c;
-
- if ( rcvlen )
- {
- if ( sio_brkmode && sio_break )
- {
- sio_break=0;
- return-2;
- }
-
- c=get_rcv_que();
- }
- else
- c=-1;
-
- return c;
- }
-
-
- /* ---------------------- peek_232() ------------------ November 19,1992 */
- int peek_232(void)
- {
- int c;
-
- if ( rcvlen == 0 )
- return(-1);
-
- c=rcvbuf[rcvhead];
-
- return(c);
-
- }
-
- /* ---------------------- peek_232x() ------------------ October 21,1993 */
- int peek_232x(void)
- {
- int c;
-
- if ( xmitlen == 0 )
- return(-1);
-
- c=xmitbuf[xmithead];
-
- return(c);
-
- }
-
-
- /* wait for char returns -2 for break */
- int get_232_wait(void)
- {
- int c;
-
- while ( rcvlen == 0 )
- if ( sio_brkmode && sio_break )
- {
- sio_break=0;
- return-2;
- }
-
- c=get_rcv_que();
-
- return c;
- }
-
-
-
- /*
- startup comport.
- You can specify the comport (1,2,3,4) or a port address
- (i.e. 0x3f8). If you specify a port you must specify irq.
- If you specify the comport and irq is zero, the default is
- used.
-
- CANNOT BE CALLED TWICE
-
- IF YOU WANT TO CALL IT AGAIN CALL deinit_232() first!!!
-
- */
-
- int init_232(serial_232_t *sr)
- {
- int com;
- // unsigned far *peekad;
- unsigned cntlword;
-
- /* point to table of COM addresses */
- // peekad=MK_FP(0x40,0);
- if ( sr->comport >= 1 && sr->comport <= 4 ) /* comm 1 ..4 */
- {
- com=sr->comport - 1;
- // uart_adrs=peekad[com]; /* get base address */
- uart_adrs=irq_adr_table[com]; /* get base address */
- if ( !uart_adrs )
- {
- sio_error=SIO_NO_COMM;
- printf("no such comm port\n");
- exit(1);
- return 1; /* not installed */
- }
-
- /* set default IRQ */
- /* set IRQ=4 for COM1/3, 3 for COM 2/4 */
- if ( !sr->irq )
- sr->irq=(com & 1) == 1 ? 3 : 4;
- }
- else
- {
- /* port Address, not comm1 .. 4 */
- /* explicit I/O set must have IRQ */
- if ( !sr->irq )
- {
- printf("bad IRQ number\n");
- exit(1);
- sio_error=SIO_NO_IRQ;
- return 1;
- }
-
- uart_adrs=sr->comport;
- }
-
- if ( sr->baud < 0 || sr->baud > sizeof(baud_table)/sizeof(int) )
- {
- printf("Invalid Baud Rate\n");
- exit(1);
- sio_error=SIO_BAD_BAUD;
- return 1;
- }
-
- if ( sr->bits < 5 || sr->bits > 8 )
- {
- printf("Bad number of data bits\n");
- exit(1);
- sio_error=SIO_BAD_BITS;
- return 1;
- }
- if ( sr->stops < 1 || sr->stops > 2 )
- {
- printf("bad number of stop bits\n");
- exit(1);
- return 1;
- }
- if ( sr->parity < 0 || sr->parity > 4 )
- {
- printf("Invalid parity\n");
- exit(1);
- sio_error=SIO_BAD_PARITY;
- return 1;
- }
-
- irqnum=sr->irq;
-
- /* calculate irq mask bit for PIC */
- irqmask=1<<sr->irq;
- oldirq=getvect(sr->irq+8); /* get old IRQ vector */
- setvect(sr->irq+8,comint); /* instal serial interupt handler */
- outportb(uart_adrs+LCR,0x83); /* none/8/1 - DLAB set */
-
- /* set baud rate */
- outportb(uart_adrs+DATA_REG,baud_table[sr->baud]&0xFF);
- outportb(uart_adrs+IER,baud_table[sr->baud]>>8);
-
- /* calculate control word for LCR */
- cntlword=(2*sr->parity-sr->parity?1:0)<<4;
- cntlword|=2*(sr->stops-1);
- cntlword|=sr->bits-5;
- outportb(uart_adrs+LCR,cntlword);
- outportb(uart_adrs+MCR,0xF); /* enable interrupts */
- outportb(uart_adrs+IER,0xF);
- outportb(0x21,inportb(0x21)&~irqmask);
-
- return 0;
- }
-
-
- /*
- CALL THIS FUNCTION BEFORE PROGRAM EXIT!
-
- or before calling init_232() for a second time
- */
- void deinit_232(void)
- {
- outportb(uart_adrs+IER,0); /* clear UART interrupts */
- outportb(uart_adrs+MCR,0);
- outportb(0x21,inportb(0x21)|irqmask); /* clear PIC */
- setvect(irqnum+8,oldirq); /* restore interrupt vector */
- }
-
- /* Send a break */
- void sio_sendbreak(void)
- {
- outportb(uart_adrs+LCR,inportb(uart_adrs+LCR)|0x40);
- t_delay(BRKTIME);
- outportb(uart_adrs+LCR,inportb(uart_adrs+LCR)&~0x40);
- }
-
- /* set DTR - never tested - let me know if it works :) */
- void sio_setdtr(short set)
- {
- unsigned char mcr;
-
- mcr=inportb(uart_adrs + MCR);
- if ( set )
- mcr|=MCR_RTS;
- else
- mcr&= ~MCR_RTS;
-
- outportb(uart_adrs + MCR, mcr);
- }
-
-
- /* ----------------------- end of basic serial stuff ---------------- */
-
- /* ================================================================== */
-
-
- /* end of serio.c ---------------------------------------------------- */
-